#include "logindialog.h"
#include "ui_logindialog.h"

using namespace std;

LoginDialog::LoginDialog(QWidget *parent)
    : QDialog(parent)
    , ui(new Ui::LoginDialog)
{
    ui->setupUi(this);


    // 设置输入密码的QLineEdit为Password模式
    ui->pass_edit->setEchoMode(QLineEdit::Password);

    // 在LoginDialog的构造函数中，connect注册按钮信号切换，发送给MainWindow用来切换界面
    connect(ui->register_btn, &QPushButton::clicked, this, &LoginDialog::switchRegister);

    // 重置密码
    ui->forget_label->SetState("normal", "hover", "", "selected", "selected_hover", "");
    ui->forget_label->setCursor(Qt::PointingHandCursor);
    connect(ui->forget_label, &ClickedLabel::clicked, this, &LoginDialog::slot_forget_pwd);
    // 密码和确认密码是否显示的设置
    ui->pass_visible->setCursor(Qt::PointingHandCursor);
    // 注意设置 pass_visible 提升为 ClickedLabel
    ui->pass_visible->SetState("unvisible", "unvisible_hover", "", "visible", "visible_hover", "");
    // 连接点击事件
    connect(ui->pass_visible, &ClickedLabel::clicked, this, [this](){
        auto state = ui->pass_visible->GetCurState();
        if(state == ClickLbState::Normal)
            ui->pass_edit->setEchoMode(QLineEdit::Password);
        else
            ui->pass_edit->setEchoMode(QLineEdit::Normal);
        qDebug() << "pass_visible was clicked!";
    });

    // 登陆
    initHandlers(); // 注册登陆http请求完成后的回调
    connect(HttpMgr::GetInstance().get(), &HttpMgr::sig_login_mod_finish, this, &LoginDialog::slot_login_mod_finish); // HttpMgr单例类代我们http请求完成，触发我们设置的回调槽函数
    // 连接tcp连接请求的信号和槽函数
    connect(this, &LoginDialog::sig_tcp_connect, TcpMgr::GetInstance().get(), &TcpMgr::slot_tcp_connect);
    // 连接tcp管理者发出的连接成功信号
    connect(TcpMgr::GetInstance().get(), &TcpMgr::sig_con_success, this, &LoginDialog::slot_tcp_con_finish);
    // 连接tcp管理者发出的登陆失败信号
    connect(TcpMgr::GetInstance().get(), &TcpMgr::sig_login_failed, this, &LoginDialog::slot_login_failed);
    // 连接tcp管理者发出的登陆成功信号 ---> 切换到聊天界面
    connect(TcpMgr::GetInstance().get(), &TcpMgr::sig_login_success, this, &LoginDialog::slot_login_success);
    // 创建定时器
    _enterChatDialogTimer = new QTimer(this);
    // 连接信号和槽
    connect(_enterChatDialogTimer, &QTimer::timeout, [this](){
        if(_count == 0){
            _enterChatDialogTimer->stop();
            emit SigSwitchChat(); // LoginDialog ---> MainWindow
            return;
        }
        _count--;
        auto str = QString("聊天许可, %1 s后进入ChatDialog界面").arg(_count);
        ui->err_tip->setText(str);
    });
}
LoginDialog::~LoginDialog(){
    delete ui;
}

// 点击忘记密码ClikcedLabel，触发的槽函数 ===> 切换到 ResetDialog 界面去重置密码
void LoginDialog::slot_forget_pwd(){
    qDebug() << "slot forget pwd";
    emit switchReset();
}

// this ===> HttpMgr    点击登陆按钮
void LoginDialog::on_login_btn_clicked(){
    qDebug() << "login btn clicked";
    // LoginDialog界面输入框的验证
    if(checkUserValid() == false) return;
    if(checkPwdValid() == false) return;

    // 发送http登陆请求
    auto user = ui->user_edit->text();
    auto pwd = ui->pass_edit->text();
    QJsonObject json_obj;
    json_obj["user"] = user;
    json_obj["passwd"] = xorString(pwd); // 密码等重要的数进行加密传输

    // HttpMgr代我们发送http请求server
    HttpMgr::GetInstance()->PostHttpReq(QUrl(gate_url_prefix + "/user_login"), json_obj, ReqId::ID_USER_LOGIN, Modules::LOGINMOD);
}

// 添加请求完成的处理逻辑
void LoginDialog::initHandlers(){
    // 登陆请求完成后的处理逻辑
    _handlers.insert(ReqId::ID_USER_LOGIN, [this](QJsonObject jsonObj){
        int error = jsonObj["error"].toInt();
        if(error != ErrorCodes::SUCCESS){
            showTip(tr("参数错误"), false);
            // ui->login_btn->setEnabled(true); // debug: 登陆成功之后，设置登陆按钮不可再被点击（不然会造成StatusServer奔溃的哦）
            return;
        }

        ui->login_btn->setEnabled(false); // debug: 登陆成功之后，设置登陆按钮不可再被点击（不然会造成StatusServer奔溃的哦）

        showTip(tr("登陆成功"), true);

        // 这里就可以获取GateServer传回来的数据了
        ServerInfo chatServer;
        chatServer.uid = jsonObj["uid"].toInt();
        // 客户端可用于建立长连接的 ChatServer 的 host 和 port 和 token 验证(注意host，port,token都是字符串的哦)
        chatServer.Host = jsonObj["host"].toString();
        chatServer.Port = jsonObj["port"].toString();
        chatServer.Token = jsonObj["token"].toString();
        qDebug() << "ChatServer: host = " << chatServer.Host << ' ' << "port = " << chatServer.Port << ' ' << "token = " << chatServer.Token;

        //
        _uid = chatServer.uid;
        _token = chatServer.Token;
        emit sig_tcp_connect(chatServer); // 使用下面的方式直接调用也是可以的
        // TcpMgr::GetInstance()->slot_tcp_connect(chatServer);
    });
}

// HttpMgr ===> this
void LoginDialog::slot_login_mod_finish(ReqId id, QString res, ErrorCodes err){
    if(err != ErrorCodes::SUCCESS){
        showTip(tr("网络请求错误"), false);
        return;
    }

    // 解析 JSON 字符串，res 需转化为QByteArray
    QJsonDocument jsonDoc = QJsonDocument::fromJson(res.toUtf8());
    // json 解析错误
    if(jsonDoc.isNull() == true){
        showTip(tr("json isNull 解析错误"), false);
        return;
    }
    if(jsonDoc.isObject() == false){
        showTip(tr("json isObject 解析错误"), false);
        return;
    }

    // 执行回调处理函数
    _handlers[id](jsonDoc.object());
    return;
}

// TcpMgr 连接 ChatServer 成功信号
void LoginDialog::slot_tcp_con_finish(bool bsuccess){
    if(bsuccess){
        showTip(tr("聊天服务连接成功，正在登录..."),true);
        QJsonObject jsonObj;
        jsonObj["uid"] = _uid;
        jsonObj["token"] = _token;

        QJsonDocument doc(jsonObj);
        QString jsonString = doc.toJson(QJsonDocument::Indented);

        // 发送tcp请求给chat server
        TcpMgr::GetInstance()->sig_send_data(ReqId::ID_CHAT_LOGIN, jsonString);
    }else{
        showTip(tr("网络异常"), false);
        ui->login_btn->setEnabled(true);
    }
}
// 请求建立长连接聊天请求失败的处理
void LoginDialog::slot_login_failed(int errcode){
    QString result = QString("登陆失败，err is %1").arg(errcode);

    showTip(result, true);
    ui->login_btn->setEnabled(true); // 使得可以再按登陆按钮
}
// TcpMgr ---> LoginDialog 请求建立长连接聊天请求成功 ===> 通过 MainWindow
void LoginDialog::slot_login_success(){
    qDebug() << "LoginDialog 已通知 MainWindow 正在切换到聊天界面......";
    _enterChatDialogTimer->start(1000); // LoginDialog 界面切换到 ChatDialog 界面先来个 _count 秒的延时
    // emit SigSwitchChat(); // LoginDialog ---> MainWindow(写到上面的定时完成后触发，不然上面冒充休眠是没有效果的)
}


// ui->tip_lb 提示 QLabel 的显示内容设置
void LoginDialog::showTip(QString errstr, bool b_ok){
    if(b_ok == true)
        ui->err_tip->setProperty("state", "normal");
    else
        ui->err_tip->setProperty("state", "err");

    ui->err_tip->setText(errstr);
    repolish(ui->err_tip);
}

// 登陆界面输入框的验证
bool LoginDialog::checkUserValid(){
    auto user = ui->user_edit->text();
    if(user.isEmpty() == true){
        AddTipErr(TipErr::TIP_USER_ERR, tr("用户名不能为空"));
        return false;
    }
    return true;
}
bool LoginDialog::checkPwdValid(){
    auto pwd = ui->pass_edit->text();
    if(pwd.length() < 4 || pwd.length() > 30){
        AddTipErr(TipErr::TIP_PWD_ERR, tr("密码长度应为4~30"));
        return false;
    }
    return true;
}


// （err_tip 提示框的设置）添加和删除错误
void LoginDialog::AddTipErr(TipErr err, QString tip){
    _tip_errs[err] = tip;
    showTip(tip, false);
}
void LoginDialog::DelTipErr(TipErr err){
    // 移除前加上是否为空的判断，不然会引发奔溃；估计是先执行了DelTipErr
    if(_tip_errs.empty() == false)
        _tip_errs.remove(err);
    // 没有错误的话，ui->err_tip 错误提示框就不用显示内容了
    if(_tip_errs.empty() == true){
        ui->err_tip->clear();
        return;
    }
    showTip(_tip_errs.first(), false);
}


